import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;


import org.mozilla.javascript.Parser;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.ast.AstRoot;

/*
 * "Exception in thread "main" java.util.MissingResourceException: 
 * Can't find bundle for base name org.mozilla.javascript.resources.Messages, 
 * locale en_US" means one of the JavaScript files had a "let", "with", "yield"
 * statement.
 */
public class Main 
{	
	/**
	 * 
	 * @param features
	 * @param file
	 * @throws IOException
	 */
	private static void logLevelFeatures(Map<String, ArrayList<String>> features, 
										 	File file) throws IOException
	{
		PrintWriter outFile = new PrintWriter(new FileWriter(file));
		
		for(Map.Entry<String, ArrayList<String>> entry : features.entrySet())
		{
			outFile.println(entry.getKey());
			for(String text : entry.getValue())
				outFile.println("\t" + text);
			
			outFile.println();
		}
		
		outFile.close();
	}
	
	@SuppressWarnings("unchecked")
	public static void main(String[] args) throws IOException, 
											ClassNotFoundException  
	{	
		if(args.length != 3)
		{
			System.err.println("Usage is as follows: \"java Main " +
					"<input directory> <output directory> <mode>\"");
			return;
		}
		
		String extractionDirName = args[1];
		File extractionDir = new File(extractionDirName);
		String DELIMITER = " # END OF FEATURE #";
		try
		{
		    if(!extractionDir.exists())
		    	extractionDir.mkdirs();
		}
		catch(Exception e)
		{
			System.err.println("Could not create \"" + extractionDirName + 
					"\" directory");
		}
		
		File flat = new File(extractionDirName + "/Flat-Level.log");
		File oneLevel = new File(extractionDirName + "/One-Level.log");
		File nLevel = new File(extractionDirName + "/N-Level.log");			
		
		File jsFileDirPath;
		
		
		jsFileDirPath = new File(args[0]);
		
		if(!jsFileDirPath.isDirectory())
		{
			System.err.println(args[0] + " is not a directory");
			return;
		}
		
		double startTime = System.currentTimeMillis() / 1000.0;
		
		System.out.println("Now extracting features...\n");
		Map<String, Integer> featureCount = 
				new LinkedHashMap<String, Integer>();
		ArrayList<String> featureList = new ArrayList<String>();

		// Record the unique number of features for each class
		Map<String, Integer> classUniques = 
				new LinkedHashMap<String, Integer>();
		// Map each class to a integer label, e.g. B1 -> 0, M1 -> 1, M2 -> 2
		Map<String, Integer> classLabel =
				new LinkedHashMap<String, Integer>();
		
		try 
		{
			PrintWriter outFile = new PrintWriter(new BufferedWriter(
					new FileWriter(flat)));
			PrintWriter outFileCsv = new PrintWriter(new BufferedWriter(
					new FileWriter(extractionDirName + "/Flat-Level-Count.csv")));
			
			//Get all directories in current dir
			String[] directories = jsFileDirPath.list(new FilenameFilter() {
				  @Override
				  public boolean accept(File dir, String name) {
				    return new File(dir, name).isDirectory();
				  }
				});
			Arrays.sort(directories);
			
			// The class label counter
			int labelCounter = 0;
			for(String strDir : directories)
			{
				File directory = new File(jsFileDirPath, strDir);
				FeatureExtractor fe = new FeatureExtractor();
				
				for(File file : directory.listFiles()) 
				{
					System.out.println("Extracting from " + file);
					Reader reader = new FileReader(file);
					AstRoot root;
					
					try
					{
						root = new Parser().parse(
								reader, file.getName(), 1);									
						root.visit(fe);
					}
					catch (EvaluatorException e)
					{
						System.err.println("Error on: " + file);
					}
											
					reader.close();
				}
				
				//outFile.println("START OF NEW WEB PAGE");
				//First char of dir is (M/B)
				String urlClass = strDir.split(".")[0];
				if(!classLabel.containsKey(urlClass))
				{
					classLabel.put(labelCounter);
					labelCounter = labelCounter + 1;
				}
				outFileCsv.print(classLabel.get(urlClass) + ",");

				for(String feature : fe.getFlatLevelFeatures())
				{
					if(!featureCount.containsKey(feature))
					{
						featureList.add(feature);
						// Add new feature to map
						featureCount.put(feature, 1);
						
						if (classUniques.containsKey(urlClass))
						{
							classUniques.put(urlClass,
									classUniques.get(urlClass) + 1);
						}
						else
						{
							classUniques.add(urlClass);
							classUniques.put(urlClass, 1);
						}
					}
					// If feature is already present, then increment counter
					else
					{
						featureCount.put(feature, 
								featureCount.get(feature) + 1);
					}

				}
				
				for(String feature : featureList)
				{
					outFileCsv.print(featureCount.get(feature) + ",");
					featureCount.put(feature, 0); //Clear value for next URL 
				}
				outFileCsv.println();
				
				fe.clearFlatLevelFeatures();
				
				logLevelFeatures(fe.getOneLevelFeatures(), oneLevel);
				fe.clearOneLevelFeatures();
				
				logLevelFeatures(fe.getNLevelFeatures(), nLevel);
				fe.clearNLevelFeatures();
				System.out.println("Finished extracting from " + strDir + "\n");
				
			}
			outFileCsv.close();
			
			// Output features in order of list
			for(String feature : featureList)
			{
				outFile.println(feature + DELIMITER);
			}
			
			outFile.close();
		}
		catch(IOException e)
		{
			System.err.println("File IO Error");
			e.printStackTrace();
		}
		catch(Exception e)
		{
			System.err.println("Error Parsing the AST");
			e.printStackTrace();
		}	
		
		System.out.println("Extraction is finished\n");
		for (String urlClass : classLabel.keySet())
		{
			System.out.println(urlClass + " is mapped to label: " + classLabel.get(urlClass));
		}
		int totalUniques = 0;
		for (String urlClass : classUniques.keySet())
		{
			System.out.println(urlClass + " Uniques: " + classUniques.get(urlClass));
			totalUniques = totalUniques + classUniques.get(urlClass);
		}
		System.out.println("Total Uniques: " + totalUniques);
		double timeAtExtractionFinish = System.currentTimeMillis() / 1000.0;
		double extractionTime = timeAtExtractionFinish - startTime;
		System.out.println("\nTime it took to finish extracting features: " +
							extractionTime / 60 + " minutes\n");
		
	}
}
